#pragma once
#include<iostream>
#include <unordered_map>
#include <websocketpp/server.hpp>
#include <websocketpp/config/asio_no_tls.hpp>
#include "log.hpp"
#include <utility>
#include "online.hpp"
typedef websocketpp::server<websocketpp::config::asio> server;

typedef enum{
    UNLOGIN,
    LOGIN
} status;
class Session{
public:
    Session(uint64_t ssid):_ssid(ssid)
    {}
    void setuid(uint64_t uid){ _uid = uid;};
    void setstatus(status s){ _status = s;};
    void settimeptr(server::timer_ptr tp){ _tp = tp;};

    uint64_t getuid(){ return _uid;}
    status getstatus(){ return _status;}
    uint64_t  getssid() { return _ssid;}
    server::timer_ptr gettp() { return _tp;}


private:
    uint64_t _ssid; //快速定位客户端和ssion的对应
    uint64_t _uid; //标识用户
    status _status; //用户状态
    server::timer_ptr _tp;//计时器
};
#define SESSION_TIMEOUT 30000
#define SESSION_FOREVER -1
class Sessionmanager{
public:
    Sessionmanager(server* sserver) :_server(sserver),_next_ssid(0) { logmessage(NORMAL,"session管理器初始化成功"); }
    ~Sessionmanager(){ logmessage(NORMAL,"session管理器退出成功");}

    std::shared_ptr<Session> createsession(uint64_t uid,status statusinfo){
        std::unique_lock<std::mutex> mut(_mutex);
        //todo
        logmessage(DEBUG,"is_in_gamehall :%d uid:%d",online_manager::get_online_manager()->is_in_gamehall(uid),uid);
        if(online_manager::get_online_manager()->is_in_gamehall(uid) 
        || online_manager::get_online_manager()->is_in_gameroom(uid)){
            //已经存在登录
            logmessage(DEBUG,"用户重复登录，已执行顶号");
            server::connection_ptr old_con = online_manager::get_online_manager()->get_connectionptr_from_hall(uid);
            uint64_t old_ssid = _uid_to_ssid[uid];

            auto old_sesson = _sessionmap[old_ssid];
            //从_sessionmap中删除会话，且取消定时器
            if(old_sesson->gettp().get()!=nullptr){
                old_sesson->gettp()->cancel();
            }
            _sessionmap.erase(old_ssid);
            //从_uid_to_ssid中删除会话
            _uid_to_ssid.erase(uid);
            //
            server::connection_ptr con =  online_manager::get_online_manager()->get_connectionptr_from_hall(uid);
            if(con.get() == nullptr){
                con= online_manager::get_online_manager()->get_connectionptr_from_room(uid);
            }

            online_manager::get_online_manager()->exit_from_gamehall(uid);
            online_manager::get_online_manager()->exit_from_gameroom(uid);
            //

            logmessage(DEBUG,"马上执行close");
            con->close(websocketpp::close::status::normal,"账号在其他地方登录");
            logmessage(DEBUG,"删除重复会话成功");
        }
        //创建新的会话
        std::shared_ptr<Session> session(new Session(_next_ssid));
        session->setuid(uid);
        session->setstatus(statusinfo);
        
        _sessionmap.insert(std::make_pair(_next_ssid,session)); //管理起来
        _uid_to_ssid.insert(std::make_pair(uid,_next_ssid));

        
        logmessage(NORMAL,"创建会话成功,ssid:%d",_next_ssid);
        _next_ssid++;
        return session;
    }

    void remove_session(uint64_t ssid){
        std::unique_lock<std::mutex> mutex(_mutex);
        uint64_t uid = _sessionmap[ssid]->getuid();
        _sessionmap.erase(ssid);
        _uid_to_ssid.erase(uid);
        logmessage(NORMAL,"删除会话成功");
    }

    std::shared_ptr<Session> find_session(uint64_t ssid){
        std::unique_lock<std::mutex> mutex(_mutex);
        auto it = _sessionmap.find(ssid);
        if(it == _sessionmap.end()){
            std::shared_ptr<Session> p(nullptr);
            return p;
        }
        return it->second;
    }
    void append_session(uint64_t ssid,std::shared_ptr<Session> session){
        std::unique_lock<std::mutex> mutex(_mutex);
        _sessionmap.insert(make_pair(ssid,session));
        _uid_to_ssid.insert(std::make_pair(session->getuid(),ssid));
        logmessage(NORMAL,"追加会话成功,ssid:%d",ssid);
    }

    void set_session_expire_time(uint64_t ssid,int ms){
        
        std::shared_ptr<Session> session = find_session(ssid);
        if(session.get() == nullptr) return;
        //获取定时器
        
        server::timer_ptr tp = session->gettp();
        if(tp.get() == nullptr && ms == SESSION_FOREVER){
            //情况1:未设置定时器，且要设置为永久
            logmessage(DEBUG,"1设置定时器成功%d",ms);
            return;
        }
        if(tp.get() == nullptr && ms != SESSION_FOREVER){
            //情况2:未设置定时器，且要设置一定时间
            //创建定时器(一定时间后，调用函数)
            server::timer_ptr tmp = _server->set_timer(ms,std::bind(&Sessionmanager::remove_session,this,ssid));
            //设置定时器
            session->settimeptr(tmp);
            logmessage(DEBUG,"2设置定时器成功%d",ms);
            return;
        }
        if(tp.get()!=nullptr && ms == SESSION_FOREVER){
            //情况3：已设置定时器，要设置为永久：先删除定时器，再设置为永久
            tp->cancel();//取消定时器，因为这个并不是立马生效，因此需要先设置为空定时器，防止删除的调用已经在等待队列中，需要在末尾添加一个session;
            session->settimeptr(server::timer_ptr()); //设置一个空的定时器

            _server->set_timer(0,std::bind(&Sessionmanager::append_session,this,session->getssid(),session));
            logmessage(DEBUG,"3设置定时器成功%d",ms);
            return;
        }
        if(tp.get() != nullptr && ms != SESSION_FOREVER){
            //情况4
            tp->cancel();//取消定时器，因为这个并不是立马生效，因此需要先设置为空定时器，防止删除的调用已经在等待队列中，需要在末尾添加一个session;
            session->settimeptr(server::timer_ptr()); //设置一个空的定时器

            _server->set_timer(0,std::bind(&Sessionmanager::append_session,this,session->getssid(),session));

            server::timer_ptr tmp = _server->set_timer(ms,std::bind(&Sessionmanager::remove_session,this,ssid));
            //设置定时器
            session->settimeptr(tmp);
            //logmessage(DEBUG,"退出时设置定时器成功%d",ms);
            logmessage(DEBUG,"4设置定时器成功%d",ms);
            return;

        }
    }






private:
    std::mutex _mutex; //保证互斥访问
    uint64_t _next_ssid;
    std::unordered_map<uint64_t,std::shared_ptr<Session>> _sessionmap; //存储ssid和session的映射关系,后续判断cookie
    std::unordered_map<uint64_t,uint64_t> _uid_to_ssid; //存储uid到ssid的映射关，使其支持以一个ssid一个uid的关系
    server* _server;
};